package com.jilujia.web.controller;

import java.io.UnsupportedEncodingException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.validation.Valid;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.jilujia.domain.question.Answer;
import com.jilujia.domain.question.AnswerComment;
import com.jilujia.domain.question.Question;
import com.jilujia.domain.question.QuestionTag;
import com.jilujia.domain.question.service.QuestionService;
import com.jilujia.framework.domain.SearchCriteria;
import com.jilujia.framework.exception.BusinessException;
import com.jilujia.framework.exception.ResourceNotFoundException;
import com.jilujia.framework.security.JilujiaUser;
import com.jilujia.framework.web.json.JsonData;
import com.jilujia.web.json.QuestionData;

@Controller
public class QuestionsController extends AbstractWebController {

	private static final Logger LOG = LoggerFactory.getLogger(QuestionsController.class);

	@Autowired
	private QuestionService questionService;

	// @Autowired
	// private UserService userService;

	// @Autowired
	// private WebApplicationContext webApplicationContext;

	public QuestionsController() {
	}

	@RequestMapping(value = "/questions/ask", method = RequestMethod.GET)
	@Secured( { "ROLE_USER" })
	public String askQuestion(Model model) {
		model.addAttribute(questionService.createQuestion());
		return "questions/ask";
	}

	@RequestMapping(value = "/ajax/questions/ask/submit", method = RequestMethod.POST)
	@Secured( { "ROLE_USER" })
	public @ResponseBody
	QuestionData submitQuestion(Principal currentUser, @Valid Question question, BindingResult result) {
		if (result.hasErrors()) {
			QuestionData questionData = new QuestionData(1, result.getAllErrors(), null, null);
			return questionData;
		}

		JilujiaUser currentRealUser = findUserByCurrentUser(currentUser);

		List<String> tags = new ArrayList<String>();
		if (!StringUtils.isBlank(question.getTag1())) {
			tags.add(question.getTag1().trim());
		}
		if (!StringUtils.isBlank(question.getTag2())) {
			tags.add(question.getTag2().trim());
		}
		if (!StringUtils.isBlank(question.getTag3())) {
			tags.add(question.getTag3().trim());
		}
		if (!StringUtils.isBlank(question.getTag4())) {
			tags.add(question.getTag4().trim());
		}
		if (!StringUtils.isBlank(question.getTag5())) {
			tags.add(question.getTag5().trim());
		}
		question.setTags(tags);

		try {
			question = questionService.persistQuestion(question, currentRealUser.getUserId());
		} catch (BusinessException e) {

			List<ObjectError> messages = new ArrayList<ObjectError>();
			ObjectError error = new ObjectError("error", e.getMessage());
			messages.add(error);

			QuestionData questionData = new QuestionData(1, messages, null, null);
			return questionData;
		}
		QuestionData questionData = new QuestionData(0, null, question.getUserId(), question.getId());
		return questionData;

	}

	@RequestMapping(value = "/ajax/questions/answer/submit", method = RequestMethod.POST)
	@Secured( { "ROLE_USER" })
	public @ResponseBody
	QuestionData submitAnswer(Principal currentUser, @Valid Answer answer, BindingResult result) {
		if (result.hasErrors()) {
			QuestionData questionData = new QuestionData(1, result.getAllErrors(), null, null);
			return questionData;
		}

		JilujiaUser currentRealUser = findUserByCurrentUser(currentUser);

		try {
			answer = questionService.persistAnswer(answer, currentRealUser.getUserId());
		} catch (BusinessException e) {

			List<ObjectError> messages = new ArrayList<ObjectError>();
			ObjectError error = new ObjectError("error", e.getMessage());
			messages.add(error);

			QuestionData questionData = new QuestionData(1, messages, null, null);
			return questionData;
		}
		QuestionData questionData = new QuestionData(0, null, answer.getUserId(), answer.getQuestion().getId());
		return questionData;
	}

	@RequestMapping(value = "/ajax/questions/comment/submit", method = RequestMethod.POST)
	@Secured( { "ROLE_USER" })
	public @ResponseBody
	QuestionData submitComment(Principal currentUser, @Valid AnswerComment answerComment, BindingResult result) {
		if (result.hasErrors()) {
			QuestionData questionData = new QuestionData(1, result.getAllErrors(), null, null);
			return questionData;
		}

		JilujiaUser currentRealUser = findUserByCurrentUser(currentUser);

		try {
			answerComment = questionService.persistAnswerComment(answerComment, currentRealUser.getUserId());
		} catch (BusinessException e) {

			List<ObjectError> messages = new ArrayList<ObjectError>();
			ObjectError error = new ObjectError("error", e.getMessage());
			messages.add(error);

			QuestionData questionData = new QuestionData(1, messages, null, null);
			return questionData;
		}
		QuestionData questionData = new QuestionData(0, null, answerComment.getUserId(), answerComment.getAnswer()
				.getQuestion().getId());
		return questionData;
	}

	@RequestMapping(value = "/questions", method = RequestMethod.GET)
	public String list(SearchCriteria criteria, Principal currentUser, Model model) {
		LOG.debug("enter questions search");

		Page<Question> questions = questionService.findQuestionsByCriteria(criteria);
		model.addAttribute(questions.getContent());

		model.addAttribute("currentNumber", questions.getNumber() + 1);
		model.addAttribute("totalPages", questions.getTotalPages());

		return "questions/list";
	}

	@RequestMapping(value = "/questions/tags", method = RequestMethod.GET)
	public String listTag(SearchCriteria criteria, Principal currentUser, Model model) {
		LOG.debug("enter tags search");

		Page<QuestionTag> questionTags = questionService.findQuestionTagsByCriteria(criteria);
		model.addAttribute(questionTags.getContent());

		model.addAttribute("currentNumber", questionTags.getNumber() + 1);
		model.addAttribute("totalPages", questionTags.getTotalPages());

		return "tags/list";
	}

	@RequestMapping(value = "/questions/tags/{tagName}", method = RequestMethod.GET)
	public String showTag(@PathVariable String tagName, SearchCriteria criteria, Principal currentUser, Model model)
			throws UnsupportedEncodingException {
		LOG.debug("enter tags:{} search", tagName);

		QuestionTag tag = questionService.findQuestionTagByName(tagName);

		if (tag == null) {
			throw new ResourceNotFoundException(tagName);
		}

		model.addAttribute(tag);

		Page<Question> questions = questionService.findQuestionsByTagCriteria(tagName, criteria);
		model.addAttribute(questions.getContent());
		model.addAttribute("currentNumber", questions.getNumber() + 1);
		model.addAttribute("totalPages", questions.getTotalPages());

		// 相关的tag

		List<QuestionTag> questionTagList = questionService.findAssociatedQuestionTags(tag);
		model.addAttribute(questionTagList);

		return "tags/show";
	}

	@RequestMapping(value = "/questions/{id}", method = RequestMethod.GET)
	public String showQuestion(@PathVariable Long id, SearchCriteria criteria, Principal currentUser, Model model) {
		// TODO
		// 需要把返回整个Question对应的answers以及comments放在一个cache里，只要有更新question，对应的answer或者comment就更新整个cache
		// TODO
		// 注意cached的可以应该是questionId+AnswerSearchCriteria或者questionId，cache里面放Map，key是pageNo
		Question question = questionService.findQuestionById(id);

		if (question == null) {
			throw new ResourceNotFoundException(id);
		}
		// 处理tags

		List<QuestionTag> tagList = questionService.findTagsByQuestionId(id);
		question.setTagList(tagList);

		// list answer page
		Page<Answer> answers = questionService.findAnswersByCriteria(id, criteria);

		// list comment page
		Map<Long, Page<AnswerComment>> commentsMap = new HashMap<Long, Page<AnswerComment>>();
		SearchCriteria commentSearchCriteria = new SearchCriteria();
		for (Answer answer : answers.getContent()) {
			// TODO 加入answer reply count = 0的情况下，不再查询
			Long answerId = answer.getId();

			Page<AnswerComment> answerComments = questionService.findAnswerCommentsByCriteria(answerId,
					commentSearchCriteria);

			commentsMap.put(answerId, answerComments);
		}
		// retrieve view count
		Integer viewCount = questionService.findQuestionViewCountById(id);

		model.addAttribute(question);
		model.addAttribute(answers);
		model.addAttribute("commentsMap", commentsMap);
		model.addAttribute("viewCount", viewCount);

		// for answer and comment text area
		Answer newAnswer = questionService.createAnswer(question);
		AnswerComment newAnswerComment = questionService.createAnswerComment();
		model.addAttribute(newAnswer);
		model.addAttribute(newAnswerComment);

		return "questions/detail";
	}

	@RequestMapping(value = "/ajax/questions/voteup/{answerId}", method = RequestMethod.POST)
	@Secured( { "ROLE_USER" })
	public @ResponseBody
	JsonData voteUpAnswer(@PathVariable Long answerId, Principal currentUser) {
		return voteAnswer(answerId, currentUser, true);
	}

	@RequestMapping(value = "/ajax/questions/votedown/{answerId}", method = RequestMethod.POST)
	@Secured( { "ROLE_USER" })
	public @ResponseBody
	JsonData voteDownAnswer(@PathVariable Long answerId, Principal currentUser) {
		return voteAnswer(answerId, currentUser, false);
	}

	private JsonData voteAnswer(Long answerId, Principal currentUser, boolean useful) {
		JilujiaUser currentRealUser = findUserByCurrentUser(currentUser);
		try {
			questionService.voteAnswer(answerId, currentRealUser.getUserId(), useful);
		} catch (BusinessException e) {
			List<ObjectError> messages = new ArrayList<ObjectError>();
			ObjectError error = new ObjectError("error", e.getMessage());
			messages.add(error);

			JsonData jsonData = new JsonData(1, messages);
			return jsonData;
		}
		return new JsonData(0, null);
	}

	@RequestMapping(value = "/ajax/questions/qvoteup/{questionId}", method = RequestMethod.POST)
	@Secured( { "ROLE_USER" })
	public @ResponseBody
	JsonData voteUpQustion(@PathVariable Long questionId, Principal currentUser) {
		return voteQuestion(questionId, currentUser, true);
	}

	@RequestMapping(value = "/ajax/questions/qvotedown/{questionId}", method = RequestMethod.POST)
	@Secured( { "ROLE_USER" })
	public @ResponseBody
	JsonData voteDownQustion(@PathVariable Long questionId, Principal currentUser) {
		return voteQuestion(questionId, currentUser, false);
	}

	private JsonData voteQuestion(Long questionId, Principal currentUser, boolean useful) {
		JilujiaUser currentRealUser = findUserByCurrentUser(currentUser);
		try {
			questionService.voteQuestion(questionId, currentRealUser.getUserId(), useful);
		} catch (BusinessException e) {
			List<ObjectError> messages = new ArrayList<ObjectError>();
			ObjectError error = new ObjectError("error", e.getMessage());
			messages.add(error);

			JsonData jsonData = new JsonData(1, messages);
			return jsonData;
		}
		return new JsonData(0, null);
	}

}
